/*
 * jQuery UI Timepicker
 *
 * Copyright 2010-2013, Francois Gelinas
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://fgelinas.com/code/timepicker
 *
 * Depends:
 *	jquery.ui.core.js
 *  jquery.ui.position.js (only if position settings are used)
 *
 * Change version 0.1.0 - moved the t-rex up here
 *
                                                  ____
       ___                                      .-~. /_"-._
      `-._~-.                                  / /_ "~o\  :Y
          \  \                                / : \~x.  ` ')
           ]  Y                              /  |  Y< ~-.__j
          /   !                        _.--~T : l  l<  /.-~
         /   /                 ____.--~ .   ` l /~\ \<|Y
        /   /             .-~~"        /| .    ',-~\ \L|
       /   /             /     .^   \ Y~Y \.^>/l_   "--'
      /   Y           .-"(  .  l__  j_j l_/ /~_.-~    .
     Y    l          /    \  )    ~~~." / `/"~ / \.__/l_
     |     \     _.-"      ~-{__     l  :  l._Z~-.___.--~
     |      ~---~           /   ~~"---\_  ' __[>
     l  .                _.^   ___     _>-y~
      \  \     .      .-~   .-~   ~>--"  /
       \  ~---"            /     ./  _.-'
        "-.,_____.,_  _.--~\     _.-~
                    ~~     (   _}       -Row
                           `. ~(
                             )  \
                            /,`--'~\--'~\
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                             ->T-Rex<-
*/

(function ($) {
	$.extend($.ui, { timepicker: { version: "0.3.3" } });

	var PROP_NAME = 'timepicker',
        tpuuid = new Date().getTime();

	/* Time picker manager.
    Use the singleton instance of this class, $.timepicker, to interact with the time picker.
    Settings for (groups of) time pickers are maintained in an instance object,
    allowing multiple different settings on the same page. */

	function Timepicker() {
		this.debug = true; // Change this to true to start debugging
		this._curInst = null; // The current instance in use
		this._disabledInputs = []; // List of time picker inputs that have been disabled
		this._timepickerShowing = false; // True if the popup picker is showing , false if not
		this._inDialog = false; // True if showing within a "dialog", false if not
		this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
		this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
		this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
		this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
		this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class

		this.regional = []; // Available regional settings, indexed by language code
		this.regional[''] = { // Default regional settings
			hourText: 'Hour',           // Display text for hours section
			minuteText: 'Minute',       // Display text for minutes link
			amPmText: ['AM', 'PM'],     // Display text for AM PM
			closeButtonText: 'Done',        // Text for the confirmation button (ok button)
			nowButtonText: 'Now',           // Text for the now button
			deselectButtonText: 'Deselect'  // Text for the deselect button
		};
		this._defaults = { // Global defaults for all the time picker instances
			showOn: 'focus',    // 'focus' for popup on focus,
			// 'button' for trigger button, or 'both' for either (not yet implemented)
			button: null,                   // 'button' element that will trigger the timepicker
			showAnim: 'fadeIn',             // Name of jQuery animation for popup
			showOptions: {},                // Options for enhanced animations
			appendText: '',                 // Display text following the input box, e.g. showing the format

			beforeShow: null,               // Define a callback function executed before the timepicker is shown
			onSelect: null,                 // Define a callback function when a hour / minutes is selected
			onClose: null,                  // Define a callback function when the timepicker is closed

			timeSeparator: ':',             // The character to use to separate hours and minutes.
			periodSeparator: ' ',           // The character to use to separate the time from the time period.
			showPeriod: false,              // Define whether or not to show AM/PM with selected time
			showPeriodLabels: true,         // Show the AM/PM labels on the left of the time picker
			showLeadingZero: true,          // Define whether or not to show a leading zero for hours < 10. [true/false]
			showMinutesLeadingZero: true,   // Define whether or not to show a leading zero for minutes < 10.
			altField: '',                   // Selector for an alternate field to store selected time into
			defaultTime: 'now',             // Used as default time when input field is empty or for inline timePicker
			// (set to 'now' for the current time, '' for no highlighted time)
			myPosition: 'left top',         // Position of the dialog relative to the input.
			// see the position utility for more info : http://jqueryui.com/demos/position/
			atPosition: 'left bottom',      // Position of the input element to match
			// Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
			//NEW: 2011-02-03
			onHourShow: null,			    // callback for enabling / disabling on selectable hours  ex : function(hour) { return true; }
			onMinuteShow: null,             // callback for enabling / disabling on time selection  ex : function(hour,minute) { return true; }

			hours: {
				starts: 0,                  // first displayed hour
				ends: 23                    // last displayed hour
			},
			minutes: {
				starts: 0,                  // first displayed minute
				ends: 55,                   // last displayed minute
				interval: 5,                // interval of displayed minutes
				manual: []                  // optional extra manual entries for minutes
			},
			rows: 4,                        // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
			// 2011-08-05 0.2.4
			showHours: true,                // display the hours section of the dialog
			showMinutes: true,              // display the minute section of the dialog
			optionalMinutes: false,         // optionally parse inputs of whole hours with minutes omitted

			// buttons
			showCloseButton: false,         // shows an OK button to confirm the edit
			showNowButton: false,           // Shows the 'now' button
			showDeselectButton: false,       // Shows the deselect time button

			maxTime: {
				hour: null,
				minute: null
			},
			minTime: {
				hour: null,
				minute: null
			}
		};
		$.extend(this._defaults, this.regional['']);

		this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
	}

	$.extend(Timepicker.prototype, {
		/* Class name added to elements to indicate already configured with a time picker. */
		markerClassName: 'hasTimepicker',

		/* Debug logging (if enabled). */
		log: function () {
			if (this.debug)
				console.log.apply('', arguments);
		},

		_widgetTimepicker: function () {
			return this.tpDiv;
		},

		/* Override the default settings for all instances of the time picker.
        @param  settings  object - the new settings to use as defaults (anonymous object)
        @return the manager object */
		setDefaults: function (settings) {
			extendRemove(this._defaults, settings || {});
			return this;
		},

		/* Attach the time picker to a jQuery selection.
        @param  target    element - the target input field or division or span
        @param  settings  object - the new settings to use for this time picker instance (anonymous) */
		_attachTimepicker: function (target, settings) {
			// check for settings on the control itself - in namespace 'time:'
			var inlineSettings = null;
			for (var attrName in this._defaults) {
				var attrValue = target.getAttribute('time:' + attrName);
				if (attrValue) {
					inlineSettings = inlineSettings || {};
					try {
						inlineSettings[attrName] = eval(attrValue);
					} catch (err) {
						inlineSettings[attrName] = attrValue;
					}
				}
			}
			var nodeName = target.nodeName.toLowerCase();
			var inline = (nodeName == 'div' || nodeName == 'span');

			if (!target.id) {
				this.uuid += 1;
				target.id = 'tp' + this.uuid;
			}
			var inst = this._newInst($(target), inline);
			inst.settings = $.extend({}, settings || {}, inlineSettings || {});
			if (nodeName == 'input') {
				this._connectTimepicker(target, inst);
				// init inst.hours and inst.minutes from the input value
				this._setTimeFromField(inst);
			} else if (inline) {
				this._inlineTimepicker(target, inst);
			}
		},

		/* Create a new instance object. */
		_newInst: function (target, inline) {
			var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
			return {
				id: id, input: target, // associated target
				inline: inline, // is timepicker inline or not :
				tpDiv: (!inline ? this.tpDiv : // presentation div
                    $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget  ui-helper-clearfix"></div>'))
			};
		},

		/* Attach the time picker to an input field. */
		_connectTimepicker: function (target, inst) {
			var input = $(target);
			inst.append = $([]);
			inst.trigger = $([]);
			if (input.hasClass(this.markerClassName)) { return; }
			this._attachments(input, inst);
			input.addClass(this.markerClassName).
                keydown(this._doKeyDown).
                keyup(this._doKeyUp).
                bind("setData.timepicker", function (event, key, value) {
                	inst.settings[key] = value;
                }).
                bind("getData.timepicker", function (event, key) {
                	return this._get(inst, key);
                });
			$.data(target, PROP_NAME, inst);
		},

		/* Handle keystrokes. */
		_doKeyDown: function (event) {
			var inst = $.timepicker._getInst(event.target);
			var handled = true;
			inst._keyEvent = true;
			if ($.timepicker._timepickerShowing) {
				switch (event.keyCode) {
					case 9: $.timepicker._hideTimepicker();
						handled = false;
						break; // hide on tab out
					case 13:
						$.timepicker._updateSelectedValue(inst);
						$.timepicker._hideTimepicker();

						return false; // don't submit the form
						break; // select the value on enter
					case 27: $.timepicker._hideTimepicker();
						break; // hide on escape
					default: handled = false;
				}
			}
			else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
				$.timepicker._showTimepicker(this);
			}
			else {
				handled = false;
			}
			if (handled) {
				event.preventDefault();
				event.stopPropagation();
			}
		},

		/* Update selected time on keyUp */
		/* Added verion 0.0.5 */
		_doKeyUp: function (event) {
			var inst = $.timepicker._getInst(event.target);
			$.timepicker._setTimeFromField(inst);
			$.timepicker._updateTimepicker(inst);
		},

		/* Make attachments based on settings. */
		_attachments: function (input, inst) {
			var appendText = this._get(inst, 'appendText');
			var isRTL = this._get(inst, 'isRTL');
			if (inst.append) { inst.append.remove(); }
			if (appendText) {
				inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
				input[isRTL ? 'before' : 'after'](inst.append);
			}
			input.unbind('focus.timepicker', this._showTimepicker);
			input.unbind('click.timepicker', this._adjustZIndex);

			if (inst.trigger) { inst.trigger.remove(); }

			var showOn = this._get(inst, 'showOn');
			if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
				input.bind("focus.timepicker", this._showTimepicker);
				input.bind("click.timepicker", this._adjustZIndex);
			}
			if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
				var button = this._get(inst, 'button');

				// Add button if button element is not set
				if (button == null) {
					button = $('<button class="ui-timepicker-trigger" type="button">...</button>');
					input.after(button);
				}

				$(button).bind("click.timepicker", function () {
					if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
						$.timepicker._hideTimepicker();
					} else if (!inst.input.is(':disabled')) {
						$.timepicker._showTimepicker(input[0]);
					}
					return false;
				});
			}
		},

		/* Attach an inline time picker to a div. */
		_inlineTimepicker: function (target, inst) {
			var divSpan = $(target);
			if (divSpan.hasClass(this.markerClassName))
				return;
			divSpan.addClass(this.markerClassName).append(inst.tpDiv).
                bind("setData.timepicker", function (event, key, value) {
                	inst.settings[key] = value;
                }).bind("getData.timepicker", function (event, key) {
                	return this._get(inst, key);
                });
			$.data(target, PROP_NAME, inst);

			this._setTimeFromField(inst);
			this._updateTimepicker(inst);
			inst.tpDiv.show();
		},

		_adjustZIndex: function (input) {
			input = input.target || input;
			var inst = $.timepicker._getInst(input);
			inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) + 1);
		},

		/* Pop-up the time picker for a given input field.
        @param  input  element - the input field attached to the time picker or
        event - if triggered by focus */
		_showTimepicker: function (input) {
			input = input.target || input;
			if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger

			if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here

			// fix v 0.0.8 - close current timepicker before showing another one
			$.timepicker._hideTimepicker();

			var inst = $.timepicker._getInst(input);
			if ($.timepicker._curInst && $.timepicker._curInst != inst) {
				$.timepicker._curInst.tpDiv.stop(true, true);
			}
			var beforeShow = $.timepicker._get(inst, 'beforeShow');
			extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
			inst.lastVal = null;
			$.timepicker._lastInput = input;

			$.timepicker._setTimeFromField(inst);

			// calculate default position
			if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
			if (!$.timepicker._pos) { // position below input
				$.timepicker._pos = $.timepicker._findPos(input);
				$.timepicker._pos[1] += input.offsetHeight; // add the height
			}
			var isFixed = false;
			$(input).parents().each(function () {
				isFixed |= $(this).css('position') == 'fixed';
				return !isFixed;
			});

			var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };

			$.timepicker._pos = null;
			// determine sizing offscreen
			inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
			$.timepicker._updateTimepicker(inst);

			// position with the ui position utility, if loaded
			if ((!inst.inline) && (typeof $.ui.position == 'object')) {
				inst.tpDiv.position({
					of: inst.input,
					my: $.timepicker._get(inst, 'myPosition'),
					at: $.timepicker._get(inst, 'atPosition'),
					// offset: $( "#offset" ).val(),
					// using: using,
					collision: 'flip'
				});
				var offset = inst.tpDiv.offset();
				$.timepicker._pos = [offset.top, offset.left];
			}

			// reset clicked state
			inst._hoursClicked = false;
			inst._minutesClicked = false;

			// fix width for dynamic number of time pickers
			// and adjust position before showing
			offset = $.timepicker._checkOffset(inst, offset, isFixed);
			inst.tpDiv.css({
				position: ($.timepicker._inDialog && $.blockUI ?
					'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
				left: offset.left + 'px', top: offset.top + 'px'
			});
			if (!inst.inline) {
				var showAnim = $.timepicker._get(inst, 'showAnim');
				var duration = $.timepicker._get(inst, 'duration');

				var postProcess = function () {
					$.timepicker._timepickerShowing = true;
					var borders = $.timepicker._getBorders(inst.tpDiv);
					inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
					css({
						left: -borders[0], top: -borders[1],
						width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
					});
				};

				// Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
				$.timepicker._adjustZIndex(input);
				//inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);

				if ($.effects && $.effects[showAnim]) {
					inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
				}
				else {
					inst.tpDiv.show((showAnim ? duration : null), postProcess);
				}
				if (!showAnim || !duration) { postProcess(); }
				if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
				$.timepicker._curInst = inst;
			}
		},

		// This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
		// Enhancement returns maximum zindex value discovered while traversing parent elements,
		// rather than the first zindex value found. Ensures the timepicker popup will be in front,
		// even in funky scenarios like non-jq dialog containers with large fixed zindex values and
		// nested zindex-influenced elements of their own.
		_getZIndex: function (target) {
			var elem = $(target);
			var maxValue = 0;
			var position, value;
			while (elem.length && elem[0] !== document) {
				position = elem.css("position");
				if (position === "absolute" || position === "relative" || position === "fixed") {
					value = parseInt(elem.css("zIndex"), 10);
					if (!isNaN(value) && value !== 0) {
						if (value > maxValue) { maxValue = value; }
					}
				}
				elem = elem.parent();
			}

			return maxValue;
		},

		/* Refresh the time picker
           @param   target  element - The target input field or inline container element. */
		_refreshTimepicker: function (target) {
			var inst = this._getInst(target);
			if (inst) {
				this._updateTimepicker(inst);
			}
		},

		/* Generate the time picker content. */
		_updateTimepicker: function (inst) {
			inst.tpDiv.empty().append(this._generateHTML(inst));
			this._rebindDialogEvents(inst);
		},

		_rebindDialogEvents: function (inst) {
			var borders = $.timepicker._getBorders(inst.tpDiv),
                self = this;
			inst.tpDiv
			.find('iframe.ui-timepicker-cover') // IE6- only
				.css({
					left: -borders[0], top: -borders[1],
					width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
				})
			.end()
            // after the picker html is appended bind the click & double click events (faster in IE this way
            // then letting the browser interpret the inline events)
            // the binding for the minute cells also exists in _updateMinuteDisplay
            .find('.ui-timepicker-minute-cell')
                .unbind()
                .bind("click", { fromDoubleClick: false }, $.proxy($.timepicker.selectMinutes, this))
                .bind("dblclick", { fromDoubleClick: true }, $.proxy($.timepicker.selectMinutes, this))
            .end()
            .find('.ui-timepicker-hour-cell')
                .unbind()
                .bind("click", { fromDoubleClick: false }, $.proxy($.timepicker.selectHours, this))
                .bind("dblclick", { fromDoubleClick: true }, $.proxy($.timepicker.selectHours, this))
            .end()
			.find('.ui-timepicker td a')
                .unbind()
				.bind('mouseout', function () {
					$(this).removeClass('ui-state-hover');
					if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
					if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
				})
				.bind('mouseover', function () {
					if (!self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
						$(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
						$(this).addClass('ui-state-hover');
						if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
						if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
					}
				})
			.end()
			.find('.' + this._dayOverClass + ' a')
				.trigger('mouseover')
			.end()
            .find('.ui-timepicker-now').bind("click", function (e) {
            	$.timepicker.selectNow(e);
            }).end()
            .find('.ui-timepicker-deselect').bind("click", function (e) {
            	$.timepicker.deselectTime(e);
            }).end()
            .find('.ui-timepicker-close').bind("click", function (e) {
            	$.timepicker._hideTimepicker();
            }).end();
		},

		/* Generate the HTML for the current state of the time picker. */
		_generateHTML: function (inst) {
			var h, m, row, col, html, hoursHtml, minutesHtml = '',
                showPeriod = (this._get(inst, 'showPeriod') == true),
                showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
                showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
                showHours = (this._get(inst, 'showHours') == true),
                showMinutes = (this._get(inst, 'showMinutes') == true),
                amPmText = this._get(inst, 'amPmText'),
                rows = this._get(inst, 'rows'),
                amRows = 0,
                pmRows = 0,
                amItems = 0,
                pmItems = 0,
                amFirstRow = 0,
                pmFirstRow = 0,
                hours = Array(),
                hours_options = this._get(inst, 'hours'),
                hoursPerRow = null,
                hourCounter = 0,
                hourLabel = this._get(inst, 'hourText'),
                showCloseButton = this._get(inst, 'showCloseButton'),
                closeButtonText = this._get(inst, 'closeButtonText'),
                showNowButton = this._get(inst, 'showNowButton'),
                nowButtonText = this._get(inst, 'nowButtonText'),
                showDeselectButton = this._get(inst, 'showDeselectButton'),
                deselectButtonText = this._get(inst, 'deselectButtonText'),
                showButtonPanel = showCloseButton || showNowButton || showDeselectButton;

			// prepare all hours and minutes, makes it easier to distribute by rows
			for (h = hours_options.starts; h <= hours_options.ends; h++) {
				hours.push(h);
			}
			hoursPerRow = Math.ceil(hours.length / rows); // always round up

			if (showPeriodLabels) {
				for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
					if (hours[hourCounter] < 12) {
						amItems++;
					}
					else {
						pmItems++;
					}
				}
				hourCounter = 0;

				amRows = Math.floor(amItems / hours.length * rows);
				pmRows = Math.floor(pmItems / hours.length * rows);

				// assign the extra row to the period that is more densely populated
				if (rows != amRows + pmRows) {
					// Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
					if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
						amRows++;
					} else {
						pmRows++;
					}
				}
				amFirstRow = Math.min(amRows, 1);
				pmFirstRow = amRows + 1;

				if (amRows == 0) {
					hoursPerRow = Math.ceil(pmItems / pmRows);
				} else if (pmRows == 0) {
					hoursPerRow = Math.ceil(amItems / amRows);
				} else {
					hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
				}
			}

			html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';

			if (showHours) {
				html += '<td class="ui-timepicker-hours">' +
                        '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
                        hourLabel +
                        '</div>' +
                        '<table class="ui-timepicker">';

				for (row = 1; row <= rows; row++) {
					html += '<tr>';
					// AM
					if (row == amFirstRow && showPeriodLabels) {
						html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
					}
					// PM
					if (row == pmFirstRow && showPeriodLabels) {
						html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
					}
					for (col = 1; col <= hoursPerRow; col++) {
						if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
							html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
						} else {
							html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
							hourCounter++;
						}
					}
					html += '</tr>';
				}
				html += '</table>' + // Close the hours cells table
                        '</td>'; // Close the Hour td
			}

			if (showMinutes) {
				html += '<td class="ui-timepicker-minutes">';
				html += this._generateHTMLMinutes(inst);
				html += '</td>';
			}

			html += '</tr>';

			if (showButtonPanel) {
				var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
				if (showNowButton) {
					buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
                                   + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g, "\\") + '" >'
                                   + nowButtonText + '</button>';
				}
				if (showDeselectButton) {
					buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
                                   + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g, "\\") + '" >'
                                   + deselectButtonText + '</button>';
				}
				if (showCloseButton) {
					buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
                                   + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g, "\\") + '" >'
                                   + closeButtonText + '</button>';
				}

				html += buttonPanel + '</div></td></tr>';
			}
			html += '</table>';

			return html;
		},

		/* Special function that update the minutes selection in currently visible timepicker
         * called on hour selection when onMinuteShow is defined  */
		_updateMinuteDisplay: function (inst) {
			var newHtml = this._generateHTMLMinutes(inst);
			inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
			this._rebindDialogEvents(inst);
			// after the picker html is appended bind the click & double click events (faster in IE this way
			// then letting the browser interpret the inline events)
			// yes I know, duplicate code, sorry
			/*                .find('.ui-timepicker-minute-cell')
								.bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
								.bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
			*/
		},

		/*
         * Generate the minutes table
         * This is separated from the _generateHTML function because is can be called separately (when hours changes)
         */
		_generateHTMLMinutes: function (inst) {
			var m, row, html = '',
                rows = this._get(inst, 'rows'),
                minutes = Array(),
                minutes_options = this._get(inst, 'minutes'),
                minutesPerRow = null,
                minuteCounter = 0,
                showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
                onMinuteShow = this._get(inst, 'onMinuteShow'),
                minuteLabel = this._get(inst, 'minuteText');

			if (!minutes_options.starts) {
				minutes_options.starts = 0;
			}
			if (!minutes_options.ends) {
				minutes_options.ends = 59;
			}
			if (!minutes_options.manual) {
				minutes_options.manual = [];
			}
			for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
				minutes.push(m);
			}
			for (i = 0; i < minutes_options.manual.length; i++) {
				var currMin = minutes_options.manual[i];

				// Validate & filter duplicates of manual minute input
				if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) {
					continue;
				}
				minutes.push(currMin);
			}

			// Sort to get correct order after adding manual minutes
			// Use compare function to sort by number, instead of string (default)
			minutes.sort(function (a, b) {
				return a - b;
			});

			minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up

			/*
             * The minutes table
             */
			// if currently selected minute is not enabled, we have a problem and need to select a new minute.
			if (onMinuteShow &&
                (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, inst.minutes]) == false)) {
				// loop minutes and select first available
				for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
					m = minutes[minuteCounter];
					if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
						inst.minutes = m;
						break;
					}
				}
			}

			html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
                    minuteLabel +
                    '</div>' +
                    '<table class="ui-timepicker">';

			minuteCounter = 0;
			for (row = 1; row <= rows; row++) {
				html += '<tr>';
				while (minuteCounter < row * minutesPerRow) {
					var m = minutes[minuteCounter];
					var displayText = '';
					if (m !== undefined) {
						displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
					}
					html += this._generateHTMLMinuteCell(inst, m, displayText);
					minuteCounter++;
				}
				html += '</tr>';
			}

			html += '</table>';

			return html;
		},

		/* Generate the content of a "Hour" cell */
		_generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
			var displayHour = hour;
			if ((hour > 12) && showPeriod) {
				displayHour = hour - 12;
			}
			if ((displayHour == 0) && showPeriod) {
				displayHour = 12;
			}
			if ((displayHour < 10) && showLeadingZero) {
				displayHour = '0' + displayHour;
			}

			var html = "";
			var enabled = true;
			var onHourShow = this._get(inst, 'onHourShow');		//custom callback
			var maxTime = this._get(inst, 'maxTime');
			var minTime = this._get(inst, 'minTime');

			if (hour == undefined) {
				html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
				return html;
			}

			if (onHourShow) {
				enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
			}

			if (enabled) {
				if (!isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour) enabled = false;
				if (!isNaN(parseInt(minTime.hour)) && hour < minTime.hour) enabled = false;
			}

			if (enabled) {
				html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g, "\\") + '" data-hour="' + hour.toString() + '">' +
                   '<a class="ui-state-default ' +
                   (hour == inst.hours ? 'ui-state-active' : '') +
                   '">' +
                   displayHour.toString() +
                   '</a></td>';
			}
			else {
				html =
            		'<td>' +
		                '<span class="ui-state-default ui-state-disabled ' +
		                (hour == inst.hours ? ' ui-state-active ' : ' ') +
		                '">' +
		                displayHour.toString() +
		                '</span>' +
		            '</td>';
			}
			return html;
		},

		/* Generate the content of a "Hour" cell */
		_generateHTMLMinuteCell: function (inst, minute, displayText) {
			var html = "";
			var enabled = true;
			var hour = inst.hours;
			var onMinuteShow = this._get(inst, 'onMinuteShow');		//custom callback
			var maxTime = this._get(inst, 'maxTime');
			var minTime = this._get(inst, 'minTime');

			if (onMinuteShow) {
				//NEW: 2011-02-03  we should give the hour as a parameter as well!
				enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, minute]);		//trigger callback
			}

			if (minute == undefined) {
				html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
				return html;
			}

			if (enabled && hour !== null) {
				if (!isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute) enabled = false;
				if (!isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute) enabled = false;
			}

			if (enabled) {
				html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g, "\\") + '" data-minute="' + minute.toString() + '" >' +
					  '<a class="ui-state-default ' +
					  (minute == inst.minutes ? 'ui-state-active' : '') +
					  '" >' +
					  displayText +
					  '</a></td>';
			}
			else {
				html = '<td>' +
	                 '<span class="ui-state-default ui-state-disabled" >' +
	                 	displayText +
	                 '</span>' +
                 '</td>';
			}
			return html;
		},

		/* Detach a timepicker from its control.
           @param  target    element - the target input field or division or span */
		_destroyTimepicker: function (target) {
			var $target = $(target);
			var inst = $.data(target, PROP_NAME);
			if (!$target.hasClass(this.markerClassName)) {
				return;
			}
			var nodeName = target.nodeName.toLowerCase();
			$.removeData(target, PROP_NAME);
			if (nodeName == 'input') {
				inst.append.remove();
				inst.trigger.remove();
				$target.removeClass(this.markerClassName)
                    .unbind('focus.timepicker', this._showTimepicker)
                    .unbind('click.timepicker', this._adjustZIndex);
			} else if (nodeName == 'div' || nodeName == 'span')
				$target.removeClass(this.markerClassName).empty();
		},

		/* Enable the date picker to a jQuery selection.
           @param  target    element - the target input field or division or span */
		_enableTimepicker: function (target) {
			var $target = $(target),
                target_id = $target.attr('id'),
                inst = $.data(target, PROP_NAME);

			if (!$target.hasClass(this.markerClassName)) {
				return;
			}
			var nodeName = target.nodeName.toLowerCase();
			if (nodeName == 'input') {
				target.disabled = false;
				var button = this._get(inst, 'button');
				$(button).removeClass('ui-state-disabled').disabled = false;
				inst.trigger.filter('button').
                    each(function () { this.disabled = false; }).end();
			}
			else if (nodeName == 'div' || nodeName == 'span') {
				var inline = $target.children('.' + this._inlineClass);
				inline.children().removeClass('ui-state-disabled');
				inline.find('button').each(
                    function () { this.disabled = false }
                )
			}
			this._disabledInputs = $.map(this._disabledInputs,
                function (value) { return (value == target_id ? null : value); }); // delete entry
		},

		/* Disable the time picker to a jQuery selection.
           @param  target    element - the target input field or division or span */
		_disableTimepicker: function (target) {
			var $target = $(target);
			var inst = $.data(target, PROP_NAME);
			if (!$target.hasClass(this.markerClassName)) {
				return;
			}
			var nodeName = target.nodeName.toLowerCase();
			if (nodeName == 'input') {
				var button = this._get(inst, 'button');

				$(button).addClass('ui-state-disabled').disabled = true;
				target.disabled = true;

				inst.trigger.filter('button').
                    each(function () { this.disabled = true; }).end();
			}
			else if (nodeName == 'div' || nodeName == 'span') {
				var inline = $target.children('.' + this._inlineClass);
				inline.children().addClass('ui-state-disabled');
				inline.find('button').each(
                    function () { this.disabled = true }
                )
			}
			this._disabledInputs = $.map(this._disabledInputs,
                function (value) { return (value == target ? null : value); }); // delete entry
			this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
		},

		/* Is the first field in a jQuery collection disabled as a timepicker?
        @param  target_id element - the target input field or division or span
        @return boolean - true if disabled, false if enabled */
		_isDisabledTimepicker: function (target_id) {
			if (!target_id) { return false; }
			for (var i = 0; i < this._disabledInputs.length; i++) {
				if (this._disabledInputs[i] == target_id) { return true; }
			}
			return false;
		},

		/* Check positioning to remain on screen. */
		_checkOffset: function (inst, offset, isFixed) {
			var tpWidth = inst.tpDiv.outerWidth();
			var tpHeight = inst.tpDiv.outerHeight();
			var inputWidth = inst.input ? inst.input.outerWidth() : 0;
			var inputHeight = inst.input ? inst.input.outerHeight() : 0;
			var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
			var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();

			offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
			offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
			offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;

			// now check if timepicker is showing outside window viewport - move to a better place if so.
			offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
			Math.abs(offset.left + tpWidth - viewWidth) : 0);
			offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
			Math.abs(tpHeight + inputHeight) : 0);

			return offset;
		},

		/* Find an object's position on the screen. */
		_findPos: function (obj) {
			var inst = this._getInst(obj);
			var isRTL = this._get(inst, 'isRTL');
			while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
				obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
			}
			var position = $(obj).offset();
			return [position.left, position.top];
		},

		/* Retrieve the size of left and top borders for an element.
        @param  elem  (jQuery object) the element of interest
        @return  (number[2]) the left and top borders */
		_getBorders: function (elem) {
			var convert = function (value) {
				return { thin: 1, medium: 2, thick: 3 }[value] || value;
			};
			return [parseFloat(convert(elem.css('border-left-width'))),
			parseFloat(convert(elem.css('border-top-width')))];
		},

		/* Close time picker if clicked elsewhere. */
		_checkExternalClick: function (event) {
			if (!$.timepicker._curInst) { return; }
			var $target = $(event.target);
			if ($target[0].id != $.timepicker._mainDivId &&
				$target.parents('#' + $.timepicker._mainDivId).length == 0 &&
				!$target.hasClass($.timepicker.markerClassName) &&
				!$target.hasClass($.timepicker._triggerClass) &&
				$.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
				$.timepicker._hideTimepicker();
		},

		/* Hide the time picker from view.
        @param  input  element - the input field attached to the time picker */
		_hideTimepicker: function (input) {
			var inst = this._curInst;
			if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
			if (this._timepickerShowing) {
				var showAnim = this._get(inst, 'showAnim');
				var duration = this._get(inst, 'duration');
				var postProcess = function () {
					$.timepicker._tidyDialog(inst);
					this._curInst = null;
				};
				if ($.effects && $.effects[showAnim]) {
					inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
				}
				else {
					inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
					    (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
				}
				if (!showAnim) { postProcess(); }

				this._timepickerShowing = false;

				this._lastInput = null;
				if (this._inDialog) {
					this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
					if ($.blockUI) {
						$.unblockUI();
						$('body').append(this.tpDiv);
					}
				}
				this._inDialog = false;

				var onClose = this._get(inst, 'onClose');
				if (onClose) {
					onClose.apply(
						(inst.input ? inst.input[0] : null),
					   [(inst.input ? inst.input.val() : ''), inst]);  // trigger custom callback
				}
			}
		},

		/* Tidy up after a dialog display. */
		_tidyDialog: function (inst) {
			inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
		},

		/* Retrieve the instance data for the target control.
        @param  target  element - the target input field or division or span
        @return  object - the associated instance data
        @throws  error if a jQuery problem getting data */
		_getInst: function (target) {
			try {
				return $.data(target, PROP_NAME);
			}
			catch (err) {
				throw 'Missing instance data for this timepicker';
			}
		},

		/* Get a setting value, defaulting if necessary. */
		_get: function (inst, name) {
			return inst.settings[name] !== undefined ?
			inst.settings[name] : this._defaults[name];
		},

		/* Parse existing time and initialise time picker. */
		_setTimeFromField: function (inst) {
			if (inst.input.val() == inst.lastVal) { return; }
			var defaultTime = this._get(inst, 'defaultTime');

			var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
			if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }

			if (timeToParse instanceof Date) {
				inst.hours = timeToParse.getHours();
				inst.minutes = timeToParse.getMinutes();
			} else {
				var timeVal = inst.lastVal = timeToParse;
				if (timeToParse == '') {
					inst.hours = -1;
					inst.minutes = -1;
				} else {
					var time = this.parseTime(inst, timeVal);
					inst.hours = time.hours;
					inst.minutes = time.minutes;
				}
			}

			$.timepicker._updateTimepicker(inst);
		},

		/* Update or retrieve the settings for an existing time picker.
           @param  target  element - the target input field or division or span
           @param  name    object - the new settings to update or
                           string - the name of the setting to change or retrieve,
                           when retrieving also 'all' for all instance settings or
                           'defaults' for all global defaults
           @param  value   any - the new value for the setting
                       (omit if above is an object or to retrieve a value) */
		_optionTimepicker: function (target, name, value) {
			var inst = this._getInst(target);
			if (arguments.length == 2 && typeof name == 'string') {
				return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
                    (inst ? (name == 'all' ? $.extend({}, inst.settings) :
                    this._get(inst, name)) : null));
			}
			var settings = name || {};
			if (typeof name == 'string') {
				settings = {};
				settings[name] = value;
			}
			if (inst) {
				extendRemove(inst.settings, settings);
				if (this._curInst == inst) {
					this._hideTimepicker();
					this._updateTimepicker(inst);
				}
				if (inst.inline) {
					this._updateTimepicker(inst);
				}
			}
		},

		/* Set the time for a jQuery selection.
	    @param  target  element - the target input field or division or span
	    @param  time    String - the new time */
		_setTimeTimepicker: function (target, time) {
			var inst = this._getInst(target);
			if (inst) {
				this._setTime(inst, time);
				this._updateTimepicker(inst);
				this._updateAlternate(inst, time);
			}
		},

		/* Set the time directly. */
		_setTime: function (inst, time, noChange) {
			var origHours = inst.hours;
			var origMinutes = inst.minutes;
			if (time instanceof Date) {
				inst.hours = time.getHours();
				inst.minutes = time.getMinutes();
			} else {
				var time = this.parseTime(inst, time);
				inst.hours = time.hours;
				inst.minutes = time.minutes;
			}

			if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) {
				inst.input.trigger('change');
			}
			this._updateTimepicker(inst);
			this._updateSelectedValue(inst);
		},

		/* Return the current time, ready to be parsed, rounded to the closest minute by interval */
		_getCurrentTimeRounded: function (inst) {
			var currentTime = new Date(),
                currentMinutes = currentTime.getMinutes(),
                minutes_options = this._get(inst, 'minutes'),
                // round to closest interval
                adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval;
			currentTime.setMinutes(adjustedMinutes);
			return currentTime;
		},

		/*
        * Parse a time string into hours and minutes
        */
		parseTime: function (inst, timeVal) {
			var retVal = new Object();
			retVal.hours = -1;
			retVal.minutes = -1;

			if (!timeVal)
				return '';

			var timeSeparator = this._get(inst, 'timeSeparator'),
                amPmText = this._get(inst, 'amPmText'),
                showHours = this._get(inst, 'showHours'),
                showMinutes = this._get(inst, 'showMinutes'),
                optionalMinutes = this._get(inst, 'optionalMinutes'),
                showPeriod = (this._get(inst, 'showPeriod') == true),
                p = timeVal.indexOf(timeSeparator);

			// check if time separator found
			if (p != -1) {
				retVal.hours = parseInt(timeVal.substr(0, p), 10);
				retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
			}
				// check for hours only
			else if ((showHours) && (!showMinutes || optionalMinutes)) {
				retVal.hours = parseInt(timeVal, 10);
			}
				// check for minutes only
			else if ((!showHours) && (showMinutes)) {
				retVal.minutes = parseInt(timeVal, 10);
			}

			if (showHours) {
				var timeValUpper = timeVal.toUpperCase();
				if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
					retVal.hours += 12;
				}
				// fix for 12 AM
				if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
					retVal.hours = 0;
				}
			}

			return retVal;
		},

		selectNow: function (event) {
			var id = $(event.target).attr("data-timepicker-instance-id"),
                $target = $(id),
                inst = this._getInst($target[0]);
			//if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
			var currentTime = new Date();
			inst.hours = currentTime.getHours();
			inst.minutes = currentTime.getMinutes();
			this._updateSelectedValue(inst);
			this._updateTimepicker(inst);
			this._hideTimepicker();
		},

		deselectTime: function (event) {
			var id = $(event.target).attr("data-timepicker-instance-id"),
                $target = $(id),
                inst = this._getInst($target[0]);
			inst.hours = -1;
			inst.minutes = -1;
			this._updateSelectedValue(inst);
			this._hideTimepicker();
		},

		selectHours: function (event) {
			var $td = $(event.currentTarget),
                id = $td.attr("data-timepicker-instance-id"),
                newHours = parseInt($td.attr("data-hour")),
                fromDoubleClick = event.data.fromDoubleClick,
                $target = $(id),
                inst = this._getInst($target[0]),
                showMinutes = (this._get(inst, 'showMinutes') == true);

			// don't select if disabled
			if ($.timepicker._isDisabledTimepicker($target.attr('id'))) { return false }

			$td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
			$td.children('a').addClass('ui-state-active');
			inst.hours = newHours;

			// added for onMinuteShow callback
			var onMinuteShow = this._get(inst, 'onMinuteShow'),
                maxTime = this._get(inst, 'maxTime'),
                minTime = this._get(inst, 'minTime');
			if (onMinuteShow || maxTime.minute || minTime.minute) {
				// this will trigger a callback on selected hour to make sure selected minute is allowed.
				this._updateMinuteDisplay(inst);
			}

			this._updateSelectedValue(inst);

			inst._hoursClicked = true;
			if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
				$.timepicker._hideTimepicker();
			}
			// return false because if used inline, prevent the url to change to a hashtag
			return false;
		},

		selectMinutes: function (event) {
			var $td = $(event.currentTarget),
                id = $td.attr("data-timepicker-instance-id"),
                newMinutes = parseInt($td.attr("data-minute")),
                fromDoubleClick = event.data.fromDoubleClick,
                $target = $(id),
                inst = this._getInst($target[0]),
                showHours = (this._get(inst, 'showHours') == true);

			// don't select if disabled
			if ($.timepicker._isDisabledTimepicker($target.attr('id'))) { return false }

			$td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
			$td.children('a').addClass('ui-state-active');

			inst.minutes = newMinutes;
			this._updateSelectedValue(inst);

			inst._minutesClicked = true;
			if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
				$.timepicker._hideTimepicker();
				// return false because if used inline, prevent the url to change to a hashtag
				return false;
			}

			// return false because if used inline, prevent the url to change to a hashtag
			return false;
		},

		_updateSelectedValue: function (inst) {
			var newTime = this._getParsedTime(inst);
			if (inst.input) {
				inst.input.val(newTime);
				inst.input.trigger('change');
			}
			var onSelect = this._get(inst, 'onSelect');
			if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
			this._updateAlternate(inst, newTime);
			return newTime;
		},

		/* this function process selected time and return it parsed according to instance options */
		_getParsedTime: function (inst) {
			if (inst.hours == -1 && inst.minutes == -1) {
				return '';
			}

			// default to 0 AM if hours is not valid
			if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends)) { inst.hours = 0; }
			// default to 0 minutes if minute is not valid
			if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }

			var period = "",
                showPeriod = (this._get(inst, 'showPeriod') == true),
                showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
                showHours = (this._get(inst, 'showHours') == true),
                showMinutes = (this._get(inst, 'showMinutes') == true),
                optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
                amPmText = this._get(inst, 'amPmText'),
                selectedHours = inst.hours ? inst.hours : 0,
                selectedMinutes = inst.minutes ? inst.minutes : 0,
                displayHours = selectedHours ? selectedHours : 0,
                parsedTime = '';

			// fix some display problem when hours or minutes are not selected yet
			if (displayHours == -1) { displayHours = 0 }
			if (selectedMinutes == -1) { selectedMinutes = 0 }

			if (showPeriod) {
				if (inst.hours == 0) {
					displayHours = 12;
				}
				if (inst.hours < 12) {
					period = amPmText[0];
				}
				else {
					period = amPmText[1];
					if (displayHours > 12) {
						displayHours -= 12;
					}
				}
			}

			var h = displayHours.toString();
			if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }

			var m = selectedMinutes.toString();
			if (selectedMinutes < 10) { m = '0' + m; }

			if (showHours) {
				parsedTime += h;
			}
			if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
				parsedTime += this._get(inst, 'timeSeparator');
			}
			if (showMinutes && (!optionalMinutes || m != 0)) {
				parsedTime += m;
			}
			if (showHours) {
				if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
			}

			return parsedTime;
		},

		/* Update any alternate field to synchronise with the main field. */
		_updateAlternate: function (inst, newTime) {
			var altField = this._get(inst, 'altField');
			if (altField) { // update alternate field too
				$(altField).each(function (i, e) {
					$(e).val(newTime);
				});
			}
		},

		_getTimeAsDateTimepicker: function (input) {
			var inst = this._getInst(input);
			if (inst.hours == -1 && inst.minutes == -1) {
				return '';
			}

			// default to 0 AM if hours is not valid
			if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends)) { inst.hours = 0; }
			// default to 0 minutes if minute is not valid
			if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }

			return new Date(0, 0, 0, inst.hours, inst.minutes, 0);
		},
		/* This might look unused but it's called by the $.fn.timepicker function with param getTime */
		/* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
		_getTimeTimepicker: function (input) {
			var inst = this._getInst(input);
			return this._getParsedTime(inst);
		},
		_getHourTimepicker: function (input) {
			var inst = this._getInst(input);
			if (inst == undefined) { return -1; }
			return inst.hours;
		},
		_getMinuteTimepicker: function (input) {
			var inst = this._getInst(input);
			if (inst == undefined) { return -1; }
			return inst.minutes;
		}
	});

	/* Invoke the timepicker functionality.
    @param  options  string - a command, optionally followed by additional parameters or
    Object - settings for attaching new timepicker functionality
    @return  jQuery object */
	$.fn.timepicker = function (options) {
		/* Initialise the time picker. */
		if (!$.timepicker.initialized) {
			$(document).mousedown($.timepicker._checkExternalClick);
			$.timepicker.initialized = true;
		}

		/* Append timepicker main container to body if not exist. */
		if ($("#" + $.timepicker._mainDivId).length === 0) {
			$('body').append($.timepicker.tpDiv);
		}

		var otherArgs = Array.prototype.slice.call(arguments, 1);
		if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute'))
			return $.timepicker['_' + options + 'Timepicker'].
			    apply($.timepicker, [this[0]].concat(otherArgs));
		if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
			return $.timepicker['_' + options + 'Timepicker'].
                apply($.timepicker, [this[0]].concat(otherArgs));
		return this.each(function () {
			typeof options == 'string' ?
			$.timepicker['_' + options + 'Timepicker'].
				apply($.timepicker, [this].concat(otherArgs)) :
			$.timepicker._attachTimepicker(this, options);
		});
	};

	/* jQuery extend now ignores nulls! */
	function extendRemove(target, props) {
		$.extend(target, props);
		for (var name in props)
			if (props[name] == null || props[name] == undefined)
				target[name] = props[name];
		return target;
	};

	$.timepicker = new Timepicker(); // singleton instance
	$.timepicker.initialized = false;
	$.timepicker.uuid = new Date().getTime();
	$.timepicker.version = "0.3.3";

	// Workaround for #4055
	// Add another global to avoid noConflict issues with inline event handlers
	window['TP_jQuery_' + tpuuid] = $;
})(jQuery);